﻿namespace DeltaGroup.WheelOfJeopardy.GameManager
{
    using System;
    using System.Collections.Generic;
    using System.Linq;

    using DeltaGroup.WheelOfJeopardy.Model;
    using DeltaGroup.WheelOfJeopardy.UI.ViewModel;

    /// <summary>
    ///     A Wheel of Jeopardy game. Responsible for generating challenges and coordinate high level game flow
    /// </summary>
    public class Game
    {
        #region Fields

        private static IGameSettings _settings;

        #endregion Fields

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="Game"/> class.
        /// </summary>
        /// <param name="level">The level.</param>
        /// <param name="jeopardy">The jeopardy.</param>
        /// <param name="doubleJeopardy">The double jeopardy.</param>
        /// <param name="finalJeopardy">The final jeopardy.</param>
        /// <param name="contestants">The contestants.</param>
        private Game(DifficultyLevel level, NormalRound jeopardy, NormalRound doubleJeopardy, FinalJeopardyRound finalJeopardy,
            List<Contestant> contestants)
        {
            Difficulty = level;
            Jeopardy = jeopardy;
            DoubleJeopardy = doubleJeopardy;
            FinalJeopardy = finalJeopardy;
            Contestants = contestants;
        }

        #endregion Constructors

        #region Properties

        /// <summary>
        /// Gets or sets the current.
        /// </summary>
        /// <value>The current.</value>
        public static Game Current
        {
            get; private set;
        }

        /// <summary>
        /// Gets the settings.
        /// </summary>
        /// <value>The settings.</value>
        public static IGameSettings Settings
        {
            get
            {
                if (_settings == null)
                    _settings = new GameSettings();
                return _settings;
            }
            set
            {
                _settings = value;
            }
        }

        /// <summary>
        /// Gets or sets the contestants.
        /// </summary>
        /// <value>The contestants.</value>
        public List<Contestant> Contestants
        {
            get; private set;
        }

        /// <summary>
        /// Gets or sets the difficulty.
        /// </summary>
        /// <value>The difficulty.</value>
        public DifficultyLevel Difficulty
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets or sets the double jeopardy.
        /// </summary>
        /// <value>The double jeopardy.</value>
        public NormalRound DoubleJeopardy
        {
            get; private set;
        }

        /// <summary>
        /// Gets or sets the final jeopardy.
        /// </summary>
        /// <value>The final jeopardy.</value>
        public FinalJeopardyRound FinalJeopardy
        {
            get; private set;
        }

        /// <summary>
        /// Gets or sets the jeopardy.
        /// </summary>
        /// <value>The jeopardy.</value>
        public NormalRound Jeopardy
        {
            get; private set;
        }

        /// <summary>
        /// Gets the ranking.
        /// </summary>
        /// <value>The ranking.</value>
        public List<Contestant> Ranking
        {
            get { return Contestants.OrderByDescending(x => x.Score).ToList(); }
        }

        #endregion Properties

        #region Methods

        /// <summary>
        /// News the specified setup.
        /// </summary>
        /// <param name="setup">The setup.</param>
        /// <returns></returns>
        public static Game New(SetupModel setup, IChallengeManager challengeManager)
        {
            var contestants = setup.Contestants.Select(x => new Contestant {Name = x.Name, Score = 0}).ToList();

            var challengeSet = challengeManager.GetChallenges(setup.SelectedChallengeFile);

            var jeopardyCategories = AssignDailyDoubles(challengeSet.JeopardyCategories, 1);
            var doubleJeopardyCategories = AssignDailyDoubles(challengeSet.DoubleJeopardyCategories, 2);

            var jeopardy = new NormalRound(Settings, jeopardyCategories, contestants, setup.Difficulty)
                               {
                                   ChallengeManager = challengeManager
                               };
            var doubleJeopardy = new NormalRound(Settings, doubleJeopardyCategories, contestants, setup.Difficulty)
                                     {
                                         ChallengeManager = challengeManager
                                     };
            var finalJeopardy = new FinalJeopardyRound(Settings, challengeSet.FinalJeopardyChallenge, contestants, setup.Difficulty)
                                    {
                                        ChallengeManager = challengeManager
                                    };

            return Current = new Game(setup.Difficulty, jeopardy, doubleJeopardy, finalJeopardy, contestants);
        }

        /// <summary>
        /// Assigns the daily doubles.
        /// </summary>
        /// <param name="categories">The categories.</param>
        /// <param name="num">The num.</param>
        /// <returns></returns>
        private static List<Category> AssignDailyDoubles(IEnumerable<Category> categories, int num)
        {
            var challenges = categories.SelectMany(x => x.Challenges).ToList();

            for (var i = 0; i < num; i++)
            {
                var r = new Random();
                var ddIndex = r.Next(0, challenges.Count);
                var ddCandidate = challenges.ElementAt(ddIndex);
                var dd = new DailyDouble(ddCandidate);
                challenges.Remove(ddCandidate);
                challenges.Add(dd);
            }

            return challenges.GroupBy(x => x.Category).Select(x => new Category
                                                                       {
                                                                           Title = x.Key,
                                                                           Challenges = x.OrderBy(y => y.Point).ToList()
                                                                       }).ToList();
        }

        #endregion Methods
    }
}